package com.kaisu.blog.domain.user;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import com.kaisu.blog.common.exceptions.InternalException;
import com.kaisu.blog.common.util.IdUtils;
import com.kaisu.blog.domain.file.File;
import com.kaisu.blog.domain.user.command.CreateUserCommand;
import com.kaisu.blog.domain.user.command.UpdateUserCommand;
import com.kaisu.blog.domain.user.vo.Password;
import lombok.Data;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;

import java.util.Collection;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @author kaisu
 */
@Data
public class User implements UserDetails {
    /**
     * 主键
     */
    private String id;
    /**
     * 头像
     */
    private File avatar;
    /**
     * 昵称
     */
    private String nickname;
    /**
     * 用户名
     */
    private String username;
    /**
     * 电话
     */
    private String phone;
    /**
     * 邮箱
     */
    private String email;
    /**
     * 密码
     */
    private Password password;
    /**
     * 是否启用
     */
    private boolean enable;
    /**
     * 角色
     */
    private Set<Role> roles;
    /**
     * 粉丝列表
     */
    private Set<Follow> followers;
    /**
     * 关注列表
     */
    private Set<Follow> followings;

    public static User create(CreateUserCommand command) {
        User user = new User();
        user.setFollowers(new HashSet<>());
        user.setFollowings(new HashSet<>());
        user.setId(IdUtils.generateId());
        BeanUtil.copyProperties(command, user, CopyOptions.create().ignoreNullValue());
        user.setPassword(Password.create(command.getPlainPassword()));
        user.enable();
        return user;
    }

    public void follow(User following) {
        if (this.getFollowings().stream().map(Follow::getFollowing).collect(Collectors.toSet()).contains(following)) {
            throw new InternalException("已经关注过该用户");
        }
        Follow follow = Follow.create(this, following);
        followings.add(follow);
        following.getFollowers().add(follow);
    }

    public void unfollow(User following) {
        Follow follow = followings.stream()
                .filter(f -> f.getFollowing().equals(following))
                .findFirst().orElseThrow();
        followings.remove(follow);
        following.getFollowers().remove(follow);
    }

    public void update(UpdateUserCommand command) {
        BeanUtil.copyProperties(command, this, CopyOptions.create().ignoreNullValue());
    }

    public void disable() {
        setEnable(false);
    }

    public void enable() {
        setEnable(true);
    }

    @Override
    public Collection<? extends GrantedAuthority> getAuthorities() {
        return roles.stream()
                .map(role -> (GrantedAuthority) () -> "ROLE_" + role.getName())
                .toList();
    }

    @Override
    public boolean isAccountNonExpired() {
        return true;
    }

    @Override
    public boolean isAccountNonLocked() {
        return true;
    }

    @Override
    public boolean isCredentialsNonExpired() {
        return true;
    }

    @Override
    public boolean isEnabled() {
        return enable;
    }

    @Override
    public String getPassword() {
        return Objects.isNull(password) ? null : password.encryptedPassword();
    }

    public boolean passwordMatches(String password) {
        return !this.password.matches(password);
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        User user = (User) o;
        return Objects.equals(id, user.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }

    public void resetPassword(String password) {
        this.setPassword(Password.create(password));
    }

    public void updatePassword(String password, String newPassword) {
        if (this.passwordMatches(password)) {
            throw new InternalException("密码错误！");
        }
        this.resetPassword(newPassword);
    }

    public void updateAvatar(File file) {
        this.setAvatar(file);
    }

    public boolean isMutual(boolean isFollow) {
        if (isFollow) {
            return this.getFollowings().stream().map(Follow::getFollower).collect(Collectors.toSet()).contains(this);
        } else {
            return this.getFollowers().stream().map(Follow::getFollowing).collect(Collectors.toSet()).contains(this);
        }
    }
}
